Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding GingerBread and Game Boy Assembly Programming for the Modern Game Developer #152

Merged
merged 3 commits into from
Feb 15, 2020

Conversation

ahrnbom
Copy link
Contributor

@ahrnbom ahrnbom commented Jan 1, 2020

Added GingerBread and the book Game Boy Assembly Programming for the Modern Game Developer (both made by me). I added a new section for "Software libraries" as GingerBread didn't really fit "Sources" or "Boilerplates" in my opinion. If you/someone feels GingerBread should belong to some other category, feel free to change it (or let me know).

Added GingerBread and the book Game Boy Assembly Programming for the Modern Game Developer. I added a new section for "Software libraries" as it didn't really fit "Sources" or "Boilerplates"...
@svendahlstrand
Copy link
Contributor

I've been waiting for this day. 😄 Thanks for all the hard work you've put into the library and book, @ahrnbom. I look forward to reading it.

@avivace avivace requested review from avivace and ISSOtm January 1, 2020 15:08
@tobiasvl
Copy link
Member

tobiasvl commented Jan 1, 2020

Not a bad idea, there's probably some other stuff that could be moved to this new section.

@avivace
Copy link
Member

avivace commented Jan 3, 2020

Oh hey! This looks really interesting and I loved the e-book organisation. That's one of the few complete resource on learning this subject.

  1. Two resources you posted need separate links (and threads), on different categories. You can however reference to one another.
  2. I'm not sure about the new suggested category "Software Libraries". Do we have any other link that we would put in there? ZGB is an example of Software Library for GBDK but it's simply listed under Tools. If we don't have anything else, we should simply put the GingerBread under the general ASM category. Another possibility would be to extend the "Boilerplates" category which, to my understanding, is very similar to what your library is doing (but to a lesser extent).

@tobiasvl Can you expand on this?

Not a bad idea, there's probably some other stuff that could be moved to this new section.

About the book:

  1. It's not released under any mainstream or recognised license. I suggest you looking at Creative Commons which may (formally) fit the idea you have described of allowed usage.
  2. Its source is not hosted anywhere. This means the community can't contribute in a easy way.
  3. I don't see a public Issue Tracker about this. There has been an ongoing discussions on the aspects of what you promote and describe as examples on Discord (@ISSOtm will gladly report to you) but we don't have nowhere to post and discuss this, a part contacting you privately. Sometimes, having a public Issue Tracker is useful even if you decide to not incorporate every suggestion we make, since Issues and PRs remain public and people can consult them as reference and gain additional insight while consuming your resource.
  4. I suggest producing a web version too. It may gain additional exposure.

About the GingerBread library:

  1. ASM templates, boilerplates and macros are generally opionated and there's already been discussion on this kind of content, mainly about not promoting optimal behaviours and/or spreading not precise information. I'm waiting @ISSOtm review on this, but I do very much like to have this exposed and specified in the description of the resource.

About adding you in the "Acknowledgements" list:

  1. That paragraph is not a contributors list. It serves to recognise people not explicity listed in the (automatically) generated list of contributors of this git repository: historical submissions and contributions that helped this project and/or the game boy development scene without having (formally) committed on this repo. Many of these people were involved since the early 2000s.
    If you take a look, that list is almost mutually exclusive to this one. You will automatically appear on the second one once you get something merged on the Awesome List.

  2. The information in 8 was never formalised, maybe we should put this somewhere (wiki meta page?).

Looking forward to hear from you! I'd really appreciate if any other @gbdev/awesome-gb member could take a look at this and hopefully give an opinion / review.

@avivace avivace added addition Suggest a new awesome resource meta Changes on the structure list, general meta discussions labels Jan 3, 2020
@ahrnbom
Copy link
Contributor Author

ahrnbom commented Jan 3, 2020

Hi!

  1. I'm not really sure what you mean here, didn't I do that already?

  2. I agree that adding a new category is probably not necessary, expanding the Boilerplates category sounds like a good solution. GingerBread is intended to be more than "just" a boilerplate but I guess in reality there isn't much of a difference.

  3. I'll consider different licenses. I'm not really sure which one fits the book the best, so I'll have to look into it a bit and make a decision. Adding some notes on usage was a last minute decision before releasing it, so I guess it was a bit rushed. Good suggestion.

4 and 5. I'll consider this too. Maybe people could open issues on GingerBread's GitHub page, even if they're about the book? I'm not sure about hosting the source to the book, but I'll consider it.

  1. I'll look into this too, but only if Latex to HTML works without jumping through too many hoops. Good suggestion.

  2. I can understand this point, and I'm sure there are several issues in GingerBread and places where I don't follow the best practices. However, I think this is somewhat besides the point. The idea behind GingerBread was to make it easier for beginners to get started. It doesn't need to optimal, it needs to be easy to get started with. At least, that was the idea, the reality is of course a different story

  3. I completely misunderstood this. I'll remove myself from this section. Sorry.

  4. Maybe it should be written right next to the paragraph, to explain in more detail what it means?

@tobiasvl
Copy link
Member

tobiasvl commented Jan 3, 2020

@tobiasvl Can you expand on this?

Well, you mentioned ZGB. I guess hardware.inc could also be considered a "software library" of sorts... dev'rs ASM section could qualify. The stuff under "Boilerplates" basically do the same thing as GingerBread.

I'm not married to the idea of a new Software Libraries section, but there's other stuff on the list that could fit together with GingerBread in one.

@ahrnbom
Copy link
Contributor Author

ahrnbom commented Jan 4, 2020

I released the source code to the book here: https://github.com/ahrnbom/gbapfomgd
I might as well link there directly. The book is now released under a CC BY-NC-SA license. This makes it easier for people to reformat it to suit their needs (ePub/mobi/html or whatever) and is a good place to open issues.

Copy link
Member

@ISSOtm ISSOtm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have noticed a bunch of issues (some objective, some subjective) while reading the book itself. I planned to write a big list once I was finished, but I will open issues on that repo instead (give me a couple days first, though). Until they are resolved either way, I'm against merging.

@ahrnbom
Copy link
Contributor Author

ahrnbom commented Jan 4, 2020

Sounds good to me. I guess I didn't understand just how curated this list was. If you guys finally decide against adding my stuff on here, that's absolutely fine.

@avivace
Copy link
Member

avivace commented Jan 10, 2020

I released the source code to the book here: https://github.com/ahrnbom/gbapfomgd . The book is now released under a CC BY-NC-SA license

Nice! Thank you for doing that!

This makes it easier for people to reformat it to suit their needs (ePub/mobi/html or whatever) and is a good place to open issues

Yes, Issues are more fit to publicy discuss and work with feedback.

If you guys finally decide against adding my stuff on here, that's absolutely fine.

I honestly want this in the list. I think it'd be a nice and costructive addition to the small set of complete book/tutorials we have.

I'll keep a watch on th repo and wait for @ISSOtm reporting Issues on the ebook and/or the library

@ISSOtm
Copy link
Member

ISSOtm commented Jan 19, 2020

I had written this twice before, but it got deleted because of software crashes. Sorry for the delay, it's just hard to muster the courage to write it all again for the third time :P

First, I do want this on the list as well, as it has a different angle from other material there, and is also more complete—my own tutorial hasn't received and update for far too long, I'll admit, but all of us are busy—so it'd be a very welcome addition.

I have, however, two remaining issues on a more ideological level. I was reluctant to bring them up initially because the last time such issues were brought up, feedback was fairly negative. Here they are:

  • Cargo cult
  • Too high-level

Let's detail.

Cargo cult

The tutorial has a companion library (GingerBread), which I do not agree wholly upon, but I've come to accept not everyone has the same programming style and habits.

The problem is something that I have heard about a specific section of the most widely-accepted GBA programming tutorial Tonc. I've heard people complain that its affine OBJ section (if memory serves right) relies too much on the library and does not explain how the library works. This problem is everywhere in gbapfomgd, since it always relies on GingerBread functions.

I'd say one of the more egregious examples is printing a string:

ld b, 150 ; End  character
ld c, 0 ; Draw to  background
ld de, 5 + 32*6 ; Position  number
ld hl, TextWithEndCharacter ; Address  to text  start
call RenderTextToEndByPosition

Here is the function's code, when it could be summed up to this:

    ld de, BACKGROUND_MAPDATA_START + 6 * 32 + 5
    ld hl, TextWithEndCharacter
.printChar
    ldh a, [rSTAT]
    and STATF_BUSY
    jr nz, .printChar
    ld a, [hli]
    cp 150
    jr z, .done
    ld [de], a
    inc de
    jr .printChar

This is much more lightweight, and does not need to be an entirely separate function, which incurs penalties for calling, computing values that are known at build time (BACKGROUND_MAPDATA_START), and register usage (bc in the code above, as well as the removal of push and pop).

This segways somewhat into the next problem, which is the tutorial staying

Too high-level

The problem seems to be trying to apply programming principles suited to compiled languages to ASM. For example, if we take the problem of the tilemap offset being known at compile time in the example above, a C compiler would be able to infer it and add the offset to de statically. This is however not possible in ASM, so the optimization has to be done by hand.

And this is another part of the problem relying too much on the library: it's an optimization and comprehension barrier, especially problematic on a system where everything is done so close to the hardware, and even hardware bugs and idiosyncracies leak through.

Another problem in this regard is the sacrifice of micro-optimization for the sake of legacy. For example, any seasoned ASM developer would have optimized the

ld b, 150 ; End  character
ld c, 0 ; Draw to  background

above to ld bc, 150 << 8 | 0, or used the following, common macro:

lb: MACRO
    ld \1, (\2) << 8 | (\3)
ENDM

This was brought up in a PR I made, where @ahrnbom replied he favors readability to optimization. I strongly disagree with this, for the reason that readability can be enhanced through the use of comments and macros (where appropriate, though), but optimization cannot.

It's important, in a tutorial explaining how to program on a 1 MHz CPU, to explain how to be efficient; instead of introducing better programming in comments, which imply they're not important, it would be a better idea to introduce the macro at some point, and keep using it throughout the rest of the tutorial.

I think GingerBread itself could benefit from a lot of such optimization—which I suppose can be accounted to some lack of experience from the author, so I wouldn't blame him—but I think there is a greater problem to it.


I have more disagreements with GingerBread itself, most notably the following three:

  • hardware.inc replacement
  • One-file restriction
  • Doing too much

hardware.inc replacement

GingerBread defines its own hardware constants, some directly lifted from hardware.inc (which is fine, it's licensed under CC0, that's not the problem here), but most are its own thing.

It's fine if you don't agree with the hardware.inc names, I can agree SVBK isn't very explicit about being the WRAMX bank switch, but GBC_VRAM_BANK_SWITCH is too long a name. It's more of a problem when using them in a library, because those are names the community de-facto globally agrees on.

Sometimes it's a good idea to make new, more explicit names, but I think the current ones are fine (if someone disagrees, please say so) and even then, the names provided in GingerBread are too verbose. This seems to be a general trend with all names, for example function names.

On a similar but unrelated note, GingerBread defines keys as d-pad in the lower nibble and buttons in the upper; this has usually been the opposite.

One-file restriction

hardware.inc is intended as a simple file that you include in all your ASM source files, wherein it will define useful constants. GingerBread also provides such constants, which implies it must be included in all source files, but it also defines data (the header) and code, which implies it can only be assembled once.

Both of these mean GingerBread can only be used in a single programming style, the "one master file to INCLUDE them all" pattern. I have strong opinions against this style with various arguments, but I'm not going to prevent someone from doing it if they want to. GingerBread, however, locks into that style for no reason; it could be split into a .inc and .asm file and work in both.

Doing too much

As I understand it, GingerBread attempts to be a sort of stdlib for GB ASM programming. I don't think this can work, mainly for the micro-optimization reason above; it works for compiled language due to linking being performed by a program (in particular smart linking, which RGBLINK cannot do at the moment, and GingerBread puts everything in giant SECTIONs anyways). This means that any function the program may not be using (example: the text-printing functions because a program wants to print characters frame by frame) will still be present in the ROM, particularly in the precious ROM0 space. If you want to remove them, you need to manually edit the library, which is a bunch of problems (pulling upstream, mostly).

Another problem is the lack of conventions in ASM; for example, VRAM-accessing functions in GingerBread disable interrupts, but that's not always a good idea, and a lot of programs don't even bother because they don't use interrupts (or a short VBlank one). It works as long as you use GingerBread functions, but it makes deviating harder and potentially buggy.

Another example: why is GBT-Player forced upon by the library? It has plenty of problems (being very basic yet fairly resource-intensive, being buggy, and maybe more), and people may not even want sound in their program, or use another driver altogether. The only way to not use it is, again, to modify the lib directly.

Or, why is GingerBread defining the header? RGBFIX does the job just fine with less source code, makes one less source file required, and leaves all the options for the user to decide (if they want to use MBC3 with RTC and no SRAM, why can't they?)

Hardcoding

I also think GingerBread and the book also encourage hardcoding too much (why is the OAM DMA code forced at $FF80? Why is the OAM DMA buffer forced at $C000? Why are the system-detection variables forced at $C1A0?). I could open issues on the respective repos about that, but after typing all of this I feel tired. :p

@pinobatch
Copy link
Member

If a program prints more than one string, the loop portion (from .printChar on down) needs to be a function. Otherwise, you have to either copy and paste the loop every time or introduce inline functions. Another word for an inline function is a macro, and you, @ISSOtm, have previously objected to "pseudo-operation" or "pseudoinstruction" macros that clobber registers (see #135).

GingerBread defines keys as d-pad in the lower nibble and buttons in the upper; this has usually been the opposite.

I personally use buttons in the lower nibble on Game Boy because it matches the nibble ordering of the Game Boy Advance and Nintendo DS input registers. But Super Game Boy ICD2 has D-pad in the lower nibble, as do the Z80-based Sega Master System and Game Gear.

@ISSOtm
Copy link
Member

ISSOtm commented Jan 19, 2020

Otherwise, you have to either copy and paste the loop every time or introduce inline functions.

A program wishing to print characters one per frame may either use a function that loops on its own, or a function that is called as part of a loop. My point is that forcing either in the lib is not a good option. Whether the user wants to call this function or variations of it is also up to them.

@ahrnbom
Copy link
Contributor Author

ahrnbom commented Jan 22, 2020

Thank you for your well written comment @ISSOtm, because it really points towards a big mistake I have made in the communication of my work. I thought putting it in the title of the book, and spending most of the first chapter on it would be sufficient, but that's clearly not the case. GingerBread itself in particular needs to communicate this point very clearly:

Both the book and GingerBread are designed with a specific audience in mind: modern game developers who are used to making games for modern platforms but have no experience with ASM or Game Boy development

If you do not belong to this group, or cannot imagine their situation, few of the decisions I made are going to make any sense, making it difficult to separate real problems from things that just make sense for one group and not for another. It seems to me that you (@ISSOtm) either didn’t understand this intention, or for whatever reason chose to ignore it, because nowhere in your discussion do you mention the learning process for these people, and this quote shows this problem clearer than anything else:

where @ahrnbom replied he favors readability to optimization

That’s not what I said at all! I never said my solution was better than yours, I said it was easier for beginners to understand which is something very different! This example appears quite early in the book, where the reader should not be expected to have such familiarity with writing ASM code, especially not highly optimized lines. That’s not to say people should never learn optimized ASM, all it means is that was not the right time to introduce it.

Modern game development libraries are designed to let the programmer focus on implementing the game logic as soon as possible. For example, a Hello World program in Löve is just three lines long. While ASM code for the Game Boy will never be as simple as that, but I tried to at least meet people half-way by only including in the book those things that are most essential to get started in making games.

If I had to write down all the details needed to get started without relying on GingerBread, it would have the following devastating issues:

  • The reader would keep looking for information about how to implement the game logic and risk losing interest long before I got there (just like most existing tutorials and documentation out there)
  • It would be really boring to write, greatly reducing the chances of it ever getting finished (again, like most tutorials out there)
  • The difference between my work and previous work would become much smaller. There’s no shortage of good material for people already familiar with the basics, and my work doesn’t exist in a vaccuum.

I therefore realized that something like GingerBread is simply a necessity in order to be able to write a (relatively) short, to-the-point tutorial that let’s people get started with implementing game logic as quickly as possible.

GingerBread also only superficially functions like a standard library: It contains a bunch of functionality that I think is useful for most games when getting started. But the similarity to standard libraries probably ends there. There is was no intention for people to not modify it for their needs, for example. So I disagree that GingerBread “forces” decisions onto people; it really doesn’t. Rather, it provides (what I consider to be) sane defaults to get people started. Once people have finished the book and maybe written a simple test game (like a Pong clone or whatever), and try to move onto more ambitious projects, the limitations of GingerBread will become obvious, but at the same time, they’ll have the basic understanding and experience to utilize all the other documentation and tutorials out there that require so much previous knowledge. Maybe they’ll start by modifying GingerBread to their needs, and finally decide they want to code their games from scratch when they feel comfortable doing so.

In other words: When GingerBread is incapable of handling the kind of game someone wants to make, and that person decides to either modify it or abandon it altogether, @ISSOtm will probably consider this a failure of the library, when in reality it is a success: it means that someone actually learned Game Boy ASM! And that’s the end goal.

With this in mind, let’s go through all the criticism @ISSOtm brought up and I’ll respond to each thing separately.

  • Cargo cult: The term “cargo cult” implies that the “modern” thinking I introduce is actually bad or not applicable, but this just comes from a lack of understanding of the target audience. Readable, understandable code is necessary for a beginner.

  • The point about the book never explaining how GingerBread works: Explaining how GingerBread works would completely remove the need for GingerBread and also destroy the book, in my opinion. GingerBread only exists to delay the learning of more complicated things so that people can start with the easy and most relevant stuff. You could of course add a chapter near the end which contains such details, but by then I think it’s easier for people to just look into the source code of GingerBread if they’re curious (which, by the way, could be better commented to make this easier, that’s something I have on my to-do list).

  • The example about printing a string, I’m sure even you agree that the code I presented is much easier to understand for someone with little to no previous ASM experience. The summed up function code is perfectly readable for an experienced ASM coder, but for a beginner it’s complete nonsense. You cannot discuss which piece of code is better or worse without discussing who it is better/worse for, because it matters a lot.

  • Being “too high level” is the whole point of the book. There’s so many (and good!) low level explanations of how the Game Boy works, like the Pan Docs.

  • About the lack of optimization, it is indeed true that I’m probably not experienced enough to properly write about optimizing Game Boy code, and especially not about best and common practices. If @ISSOtm or someone else would like to add a chapter about this somewhere near the end of the book, I think that would be a significant improvement. I do insist on not introducing optimizations that hurt readability until the reader has the experience needed to understand it.

  • Replacing hardware.inc: Indeed, I felt like hardware.inc was a bit too cryptic, especially for beginners.

  • One file restriction: This I can somewhat agree on, splitting GingerBread into inc/asm files would certainly be possible. Maybe that would discourage people from modifying GingerBread or looking in its source code? I never looked into GBT-Player’s source code, for example, only the .inc file.

  • As I mentioned, GingerBread does not attempt to be a stdlib for Game Boy. Calling it a library was probably a mistake. I’m not sure what else to call it though, because while it’s quite similar to boilerplates and templates, it’s not quite the same.

  • GBT-Player is not enforced at all. For example, the included example game can be compiled and works fine without GBT-Player, just don’t define USE_GBT_PLAYER in the code and set include_gbt to false in the assembling script.

  • About defining the header: I honestly did not know RGBFIX could take care of all of that, I thought it only handled the things you couldn’t know before compiling like the checksum. I’ll look into this, because it seems like a sane thing to fix.

  • The hardcoding stuff is there to give people a reasonable default, so that they can get started on coding the stuff that matters the most to them first.

  • About the example when you would want to write parts of a string separately, I don’t understand how you (@ISSOtm and @pinobatch) can even discuss this without taking into account where in the learning process the hypothetical programmer is. For a complete beginner, I think using GingerBread’s RenderTextToLength function where the length input varies each frame is a fair solution. It’s inefficient, sure, but it works and should be easy to understand, which is what matters the most in the beginning. A somewhat more seasoned programmer may write their own function for this, that only writes one byte at the time, independent of GingerBread’s functionality. An even more experienced programmer would maybe modify GingerBread to be more optimized for their needs. And an even more seasoned programmer probably wouldn’t use GingerBread anymore.

To summarize, I am more convinced than ever that I made several key design decisions correctly in my work. I’m not going to sacrifice that just to be included on this list. Even if you guys decide to not include my stuff on here, I hope you’ll at least consider what your definition of “awesome” is, so that it doesn’t become “only good for people who already know this stuff already”. What I do need to do is to fix some of the real problems in my book and code, and communicate better how it should be used.

@avivace avivace merged commit 0fbb21f into gbdev:master Feb 15, 2020
@avivace
Copy link
Member

avivace commented Feb 15, 2020

Martin, thank you for taking the time to handle every raised issue.
I'm merging this due to no more opposition.

This essentialy goes down to the never ending discussion between me and @ISSOtm on his radical (and basically correct) viewpoints vs me accepting "less perfect" and opinionated resources.

We have to embrance and accompany different target of users, and providing assembly optimisation tips to beginners shifting from gbdk (or starting from the ground up) risks to overhelm and turn them away. We do not want that. We are not training a crowd of assembly developers, we have to provide fashionable entry points for anyone. AND THEN make them progressively shift to more accurate and advanced styles of coding and approaches.

Politics apart, I've had the chance to read a bit of the book and I think this definitely fits the list. Even if it isn't perfect and it's opinioted in a number of ways it is a good resource in a category lacking quality content.

It would be very good to report (a kind of "disclaimer") some of the raised points by ISSO directly on the GingerBread repo (both on the book and the library, splitting them), so the users can acknowledge the opinionated parts and critical choices made in this resource, providing a more informated position on the subjects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition Suggest a new awesome resource meta Changes on the structure list, general meta discussions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants